package common

import (
	"encoding/json"
	"fmt"
	"io"
	"math"
	"math/rand"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func GenerateRandomString(length int) string {
	chars := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	result := make([]byte, length)
	for i := 0; i < length; i++ {
		index := rand.Intn(len(chars))
		result[i] = chars[index]
	}
	return string(result)
}

func GenerateRequestID() string {
	rand := rand.New(rand.NewSource(time.Now().UnixNano()))

	return strconv.Itoa(1000+rand.Intn(9000)) +
		"_" +
		strconv.Itoa(1000+rand.Intn(9000))
}

func ConnectMysql(dsn string) (*gorm.DB, error) {
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		return nil, fmt.Errorf("数据库连接失败: %w", err)
	}
	return db, nil
}

func FileStore(path string, content []byte) error {
	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
		return fmt.Errorf("创建目录失败: %w", err)
	}

	// 大文件优化 待做
	const filePerm = 0644
	err := os.WriteFile(path, content, filePerm)

	if err != nil {
		return fmt.Errorf("写入文件 %q 失败: %w", path, err)
	}

	return nil
}

func FileRead(path string) ([]byte, error) {
	// 大文件优化 待做
	return os.ReadFile(path)
}

func ByteSize(bytes int) string {
	absBytes := int(math.Abs(float64(bytes))) // 处理负数

	const (
		_  = iota             // ignore first value
		KB = 1 << (10 * iota) // 1 << (10*1) = 1024
		MB                    // 1 << (10*2) = 1048576
		GB                    // 1 << (10*3) = 1073741824
		TB                    // 1 << (10*4) = 1099511627776
		PB                    // 1 << (10*5) = 1125899906842624
		EB                    // 1 << (10*6) = 1152921504606846976
	)

	var sign string
	if bytes < 0 {
		sign = "-"
	}

	switch {
	case absBytes < KB:
		return fmt.Sprintf("%s%d B", sign, absBytes)
	case absBytes < MB:
		return fmt.Sprintf("%s%.1f KB", sign, float64(absBytes)/KB)
	case absBytes < GB:
		return fmt.Sprintf("%s%.1f MB", sign, float64(absBytes)/MB)
	case absBytes < TB:
		return fmt.Sprintf("%s%.1f GB", sign, float64(absBytes)/GB)
	case absBytes < PB:
		return fmt.Sprintf("%s%.1f TB", sign, float64(absBytes)/TB)
	case absBytes < EB:
		return fmt.Sprintf("%s%.1f PB", sign, float64(absBytes)/PB)
	default:
		return fmt.Sprintf("%s%.1f EB", sign, float64(absBytes)/EB)
	}
}

func ParseJSONRequest(r *http.Request, data interface{}) error {
	body, err := io.ReadAll(r.Body)
	if err != nil {
		return fmt.Errorf("读取请求体失败: %w", err)
	}
	defer r.Body.Close()

	if len(body) == 0 {
		return fmt.Errorf("请求体不能为空")
	}

	if err := json.Unmarshal(body, &data); err != nil {
		switch {
		case err.Error() == "http: request body too large":
			return fmt.Errorf("请求体过大")
		case strings.Contains(err.Error(), "json: unknown field"):
			field := strings.Split(err.Error(), `"`)[1]
			return fmt.Errorf("未知字段 '%s'", field)
		case strings.Contains(err.Error(), "cannot unmarshal"):
			field := ""
			if parts := strings.Split(err.Error(), "into"); len(parts) > 1 {
				field = strings.TrimSpace(strings.Split(parts[0], " ")[0])
			}
			return fmt.Errorf("类型错误: 字段 '%s' 的值格式不正确", field)
		default:
			return fmt.Errorf("无效的JSON格式: %w", err)
		}
	}

	return nil
}

func WriteProtoJSON(w http.ResponseWriter, msg proto.Message) error {
	data, err := MarshalWithoutOmitempty(msg)
	if err != nil {
		return err
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	_, err = w.Write(data)
	return err
}

func MarshalWithoutOmitempty(msg proto.Message) ([]byte, error) {
	marshaler := protojson.MarshalOptions{
		EmitUnpopulated: true,
		UseProtoNames:   false,
	}
	return marshaler.Marshal(msg)
}
